﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using CodeGeneration.Model;
using Commons.Collections;
using NVelocity;
using NVelocity.App;
using System.Linq;
using NVelocity.Runtime;
using System.ComponentModel.DataAnnotations;

namespace CodeGeneration.Tools
{
    public partial class GenerateCode
    {
        private readonly CreateConfig _config = new CreateConfig();

        public GenerateCode(string dbContext, IList<string> ignoreClassList ,string dalName = "DAL", string bllName = "BLL", bool isOverwriteFile = false)
        {
            _config = new CreateConfig
                          {
                              DALName = dalName,
                              BLLName = bllName,
                              IsOverwriteFile = isOverwriteFile,
                              DbContextType = dbContext,
                              AssemblyName = dalName,
                              IgnoreClassList = ignoreClassList
                          };
        }

        public void Start()
        {
            Console.WriteLine("读取到的配置信息如下：");
            Console.WriteLine(_config);
            Create();
        }

        private void Create()
        {
            var typesToGenerate = GetAssembly();
            if (typesToGenerate == null)
            {
                return;
            }
            foreach (var t in typesToGenerate)
            {
                _config.CurrentClassName = t.Name;
                _config.ModelNamespace = t.Namespace;

                if (_config.IgnoreClassList.Contains(t.Name))
                {
                    Console.WriteLine("忽略类：{0}", t.FullName);
                    continue;
                }

                var keyPorperty = t.GetProperties().Where(p => p.GetCustomAttributes(typeof(KeyAttribute), true).Any()).ToList();
                if (keyPorperty.Count() == 1)
                {
                    _config.PrimaryKey = keyPorperty.First().Name;
                    _config.PrimaryKeyType = keyPorperty.First().PropertyType.ToString();
                    //格式化Type类型的字符串格式
                    _config.PrimaryKeyType = FormatKeyType(_config.PrimaryKeyType);
                }
                else
                {
                    Console.WriteLine("类{0}没有定义主键或者定义的主键个数超过一个，已经跳过该类的代码生成", _config.CurrentClassName);
                    continue;
                }

                VelocityContext context = BuildContext(t);
                CreateInterface(_config, context);
                CreateImplement(_config, context);
            }
        }

        private Template BuildTemplate(string template, CreateConfig config)
        {
            VelocityEngine engine = new VelocityEngine();
            ExtendedProperties props = new ExtendedProperties();
            props.AddProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, config.TemplateFilePath);
            engine.Init(props);
            return engine.GetTemplate(template);
        }

        private VelocityContext BuildContext(Type t)
        {
            VelocityContext context = new VelocityContext();
            CheckPath(t);
            context.Put("ModelName", _config.CurrentClassName);
            context.Put("ModelNameVar", char.ToLower(_config.CurrentClassName[0]) + _config.CurrentClassName.Substring(1));
            context.Put("PrimaryKeyType", _config.PrimaryKeyType);
            context.Put("PrimaryKey", _config.PrimaryKey);
            context.Put("InterfaceNamespace", _config.InterfaceNamespace);
            context.Put("ImplementNamespace", _config.ImplementNamespace);
            context.Put("PrimaryKeyVar", char.ToLower(_config.PrimaryKey[0]) + _config.PrimaryKey.Substring(1));
            context.Put("ModelNamespace", _config.ModelNamespace);
            context.Put("DbContextType", _config.DbContextType);
            context.Put("DbContextName", char.ToLower(_config.DbContextType[0]) + _config.DbContextType.Substring(1));
            return context;
        }

        private void CheckPath(Type t)
        {
            if (t.Namespace == null)
            {
                return;
            }
            string nextLevelInterfacePath = t.Namespace
                .Replace(_config.DALName, _config.BLLName)
                .Replace(_config.ObjectModelFolder, "Interface")
                .Replace('.', '\\') + "\\";
            string nextLevelImplementPath = t.Namespace
                .Replace(_config.DALName, _config.BLLName)
                .Replace(_config.ObjectModelFolder, "Implement")
                .Replace('.', '\\') + "\\";
            _config.InterfacePath = _config.ProjectPath + nextLevelInterfacePath;
            _config.ImplementPath = _config.ProjectPath + nextLevelImplementPath;
            _config.InterfaceNamespace = nextLevelInterfacePath.Replace("\\", ".").Substring(0, nextLevelInterfacePath.Length - 1);
            _config.ImplementNamespace = nextLevelImplementPath.Replace("\\", ".").Substring(0, nextLevelImplementPath.Length - 1);
            if (!Directory.Exists(_config.InterfacePath))
            {
                Directory.CreateDirectory(_config.InterfacePath);
            }
            if (!Directory.Exists(_config.ImplementPath))
            {
                Directory.CreateDirectory(_config.ImplementPath);
            }
        }

        private IEnumerable<Type> GetAssembly()
        {
            try
            {
                Assembly asm = Assembly.Load(_config.AssemblyName);
                var typesToGenerate = asm.GetTypes().Where(p => p.Namespace != null
                                                            && p.Namespace.StartsWith(_config.DALName + "." + _config.ObjectModelFolder)
                                                      ).ToList();
                if (!typesToGenerate.Any())
                {
                    Console.WriteLine("在程序集：{0} 没有找到任何包含实体类的文件夹，通常这些类应该在文件夹：{1} 中"
                        , _config.AssemblyName, _config.ObjectModelFolder);
                    return null;
                }
                return typesToGenerate;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                return null;
            }
        }

        private string FormatKeyType(string keyTypeString)
        {
            keyTypeString = keyTypeString.Replace("System.", "");
            switch (keyTypeString)
            {
                case "Int32":
                    return "int";
                case "String":
                    return "string";
            }
            return keyTypeString;
        }

    }
}
